using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using NBear.Common;

namespace NBear.Data
{
    public sealed class DbProviderFactory
    {
        #region Private Members

        private static Dictionary<string, DbProvider> providerCache = new Dictionary<string, DbProvider>();

        /// <summary>
        /// Initializes a new instance of the <see cref="T:DbProviderFactory"/> class.
        /// </summary>
        private DbProviderFactory()
		{
        }

        #endregion

        #region Public Members

        /// <summary>
        /// Creates the db provider.
        /// </summary>
        /// <param name="assemblyName">Name of the assembly.</param>
        /// <param name="className">Name of the class.</param>
        /// <param name="connStr">The conn STR.</param>
        /// <returns></returns>
        public static DbProvider CreateDbProvider(string assemblyName, string className, string connStr)
        {
            Check.Require(!string.IsNullOrEmpty(className), null, new ArgumentNullOrEmptyException("className"));
            Check.Require(!string.IsNullOrEmpty(connStr), null, new ArgumentNullOrEmptyException("connStr"));

            string cacheKey = string.Concat(assemblyName, className, connStr);
            if (providerCache.ContainsKey(cacheKey))
            {
                return providerCache[cacheKey];
            }
            else
            {
                System.Reflection.Assembly ass;

                if (assemblyName == null)
                {
                    ass = typeof(DbProvider).Assembly;
                }
                else
                {
                    ass = System.Reflection.Assembly.Load(assemblyName);
                }

                DbProvider retProvider = ass.CreateInstance(className, false, System.Reflection.BindingFlags.Default, null, new object[] { connStr }, null, null) as DbProvider;
                providerCache.Add(cacheKey, retProvider);
                return retProvider;
            }
        }

        /// <summary>
        /// Gets the default.
        /// </summary>
        /// <value>The default.</value>
        public static DbProvider Default
        {
            get
            {
                try
                {
                    ConnectionStringSettings connStrSetting = ConfigurationManager.ConnectionStrings[ConfigurationManager.ConnectionStrings.Count - 1];
                    string[] assAndClass = connStrSetting.ProviderName.Split(',');
                    if (assAndClass.Length > 1)
                    {
                        return CreateDbProvider(assAndClass[1].Trim(), assAndClass[0].Trim(), connStrSetting.ConnectionString);
                    }
                    else
                    {
                        return CreateDbProvider(null, assAndClass[0].Trim(), connStrSetting.ConnectionString);
                    }
                }
                catch
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Creates the db provider.
        /// </summary>
        /// <param name="connStrName">Name of the conn STR.</param>
        /// <returns></returns>
        public static DbProvider CreateDbProvider(string connStrName)
        {
            Check.Require(connStrName != null, null, new ArgumentNullOrEmptyException("connStrName"));

            ConnectionStringSettings connStrSetting = ConfigurationManager.ConnectionStrings[connStrName];
            Check.Invariant(connStrSetting != null, null, new ConfigurationErrorsException(string.Concat("Cannot find specified connection string setting named as ", connStrName, " in application config file's ConnectionString section.")));
            string[] assAndClass = connStrSetting.ProviderName.Split(',');
            if (assAndClass.Length > 1)
            {
                return CreateDbProvider(assAndClass[0].Trim(), assAndClass[1].Trim(), connStrSetting.ConnectionString);
            }
            else
            {
                return CreateDbProvider(null, assAndClass[0].Trim(), connStrSetting.ConnectionString);
            }
        }

        #endregion
    }
}
